package movgen;


import java.math.BigInteger;

public class BitUtil {
	public static int WP=0;
	public static int BP=1;
	public static int WN=2;
	public static int BN=3;
	public static int WB=4;
	public static int BB=5;
	public static int WR=6;
	public static int BR=7;
	public static int WQ=8;
	public static int BQ=9;
	public static int WK=10;
	public static int BK=11;
	

	public static long inBetweenWithoutLookup(int sq1, int sq2)
	{
		//Gerd Isenberg
		final long o = 1, m1 = -o;
		final long a2a7 = 0x0001010101010100L;
		final long b7h1 = 0x0002040810204080L;
		final long b2h8 = 0x8040201008040200L;
	    long btwnbits, raybits;
	    int rankDiff, fileDiff, antiDiff, diaxDiff;   

	    btwnbits  = (o<<sq1) -  o;
	    btwnbits ^= (o<<sq2) -  o;
	    rankDiff  =((sq2 |7) -  sq1)>>3;
	    fileDiff  = (sq2 &7) - (sq1 &7);
	    antiDiff  = rankDiff + fileDiff;
	    rankDiff  = rankDiff &  15;
	    fileDiff  = fileDiff &  15;
	    antiDiff  = antiDiff &  15;
	    diaxDiff  = rankDiff ^ fileDiff;
	    raybits   =((rankDiff-1)>>>26)*2;
	    raybits  |= (m1+fileDiff)& a2a7;
	    raybits  |= (m1+antiDiff)& b7h1;
	    raybits  |= (m1+diaxDiff)& b2h8;
	    raybits  *= btwnbits &-btwnbits;
	    return      raybits  & btwnbits;
	}

	private static String[] convertAllBitBoardsToString(long[] bitboard) {
		  String[] bitString=new String[12];
		 for (int i=0; i < 12; i++) {
			 bitString[i]= BitUtil.longToBinaryString(bitboard[i]);
		 }
		 return bitString;
	}
	
  public static String[] boardToBitStrings(char[] board) {
		  
		  String[] bitString = new String[12];
		  
		    char[] wpC = new char[64];
		    char[] bpC = new char[64];
		   
		    char[] wnC = new char[64];
		    char[] bnC = new char[64];
		    
		    char[] wbC = new char[64];
		    char[] bbC = new char[64];
		    
		    char[] wrC = new char[64];
		    char[] brC = new char[64];
		    
		    char[] wqC = new char[64];
		    char[] bqC = new char[64];
		    
		    char[] wkC = new char[64];
		    char[] bkC = new char[64];
		    
			for (int i=0; i < 64; i++) 
			{
				wpC[i]='0';
				wnC[i]='0';
				wbC[i]='0';
				wrC[i]='0';
				wqC[i]='0';
				wkC[i]='0';
				
				bpC[i]='0';
				bnC[i]='0';
				bbC[i]='0';
				brC[i]='0';
				bqC[i]='0';
				bkC[i]='0';
				
				if ( board[i]=='p') bpC[i]='1';
				else 
				if ( board[i]=='P') wpC[i]='1';
				else 
				if ( board[i]=='n') bnC[i]='1';
				else 
				if ( board[i]=='N') wnC[i]='1';
				else 
				if ( board[i]=='b') bbC[i]='1';
				else 
				if ( board[i]=='B') wbC[i]='1';
				else 
				if ( board[i]=='r') brC[i]='1';
				else 
				if ( board[i]=='R') wrC[i]='1';
				else
				if ( board[i]=='q') bqC[i]='1';
				else 
				if ( board[i]=='Q') wqC[i]='1';
				else 
				if ( board[i]=='k') bkC[i]='1';
				else 
				if ( board[i]=='K') wkC[i]='1';
				
				
				//System.out.print(board[i]);
			}
			//System.out.println();
			bitString[WP]= new String(wpC);
			bitString[WN]= new String(wnC);
			bitString[WB]= new String(wbC);
			bitString[WR]= new String(wrC);
			bitString[WQ]= new String(wqC);
			bitString[WK]= new String(wkC);
			
			bitString[BP]= new String(bpC);
			bitString[BN]= new String(bnC);
			bitString[BB]= new String(bbC);
			bitString[BR]= new String(brC);
			bitString[BQ]= new String(bqC);
			bitString[BK]= new String(bkC);
			
			return bitString;
		}
  
  
  private static char[] bitStringsToBoard(String bitString[]) {
		char[] board=new char[64];
		for (int i=0; i < 64; i++) 
		{
			if (bitString[BP].charAt(i)=='1') board[i]='p';
			else 
			if (bitString[WP].charAt(i)=='1') board[i]='P';
			else 
			if (bitString[WN].charAt(i)=='1') board[i]='N';
			else 
			if (bitString[BN].charAt(i)=='1') board[i]='n';
			else 
			if (bitString[WB].charAt(i)=='1') board[i]='B';
			else 
			if (bitString[BB].charAt(i)=='1') board[i]='b';
			else
			if (bitString[WR].charAt(i)=='1') board[i]='R';
			else 
			if (bitString[BR].charAt(i)=='1') board[i]='r';
			else
			if (bitString[WQ].charAt(i)=='1') board[i]='Q';
			else 
			if (bitString[BQ].charAt(i)=='1') board[i]='q';
			else
			if (bitString[WK].charAt(i)=='1') board[i]='K';
			else 
			if (bitString[BK].charAt(i)=='1') board[i]='k';
			else board[i]='-';
			
			//System.out.print(board[i]);
		}
		//System.out.println();
		return board;
	}
  
  public static char[] bitboardsToCharBoard(long[]bitboard) {
	    String bitString[] = BitUtil.convertAllBitBoardsToString(bitboard);
		char[] board = BitUtil.bitStringsToBoard(bitString);
		return board;
  }
 
  public static String boardToString(long[] bitboard) {
		String bitString[] = BitUtil.convertAllBitBoardsToString(bitboard);
		char[] board = BitUtil.bitStringsToBoard(bitString);
		
		StringBuffer s= new StringBuffer();
		
		for (int i=0; i < 64; i++) {
			if (i % 8 == 0) s.append("\n");
			s.append(board[i] + " ");
		}
		s.append("\n");
		return s.toString();
	}
	
  public static String boardToString(char[] board) {
		
		
		StringBuffer s= new StringBuffer();
		
		for (int i=0; i < 64; i++) {
			if (i % 8 == 0) s.append("\n");
			s.append(board[i] + " ");
		}
		s.append("\n");
		return s.toString();
	}
  public static String boardToString(int[] board) {
		
		
		StringBuffer s= new StringBuffer();
		
		for (int i=0; i < 64; i++) {
			if (i % 8 == 0) s.append("\n");
			s.append(board[i] + " ");
		}
		s.append("\n");
		return s.toString();
	}
 
	private static String longToBinaryString(long val) {
		String padding = "0000000000000000000000000000000000000000000000000000000000000000";
		String result = padding + Long.toBinaryString(val);
		result = result.substring(result.length() - 64, result.length());  // take the right-most 64 digits
		return result;
	}
	
	private static long parseLong(String s, int base) {
		return new BigInteger(s, base).longValue();
	}
	public static long BitString2Long(String s) {
		return parseLong(s,2);
	}

	private static final int index64[] = {
	    0, 47,  1, 56, 48, 27,  2, 60,
	   57, 49, 41, 37, 28, 16,  3, 61,
	   54, 58, 35, 52, 50, 42, 21, 44,
	   38, 32, 29, 23, 17, 11,  4, 62,
	   46, 55, 26, 59, 40, 36, 15, 53,
	   34, 51, 20, 43, 31, 22, 10, 45,
	   25, 39, 14, 33, 19, 30,  9, 24,
	   13, 18,  8, 12,  7,  6,  5, 63
	};
  private static final long debruijn64 = 0x03f79d71b4cb0a89L;
	/**
	 * bitScanForward
	 * @author Kim Walisch (2012)
	 * @param bb bitboard to scan
	 * @precondition bb != 0
	 * @return index (0..63) of least significant one bit
	 */
	public static final int bitScanForward(long bb) {
	   bb=bb ^ (bb-1);
	   int i = (int)((bb * debruijn64) >>> 58);
	   return index64[i];
	}
	static long soutOne (long b) {return  b >> 8;}
	static long nortOne (long b) {return  b << 8;}
	
	static long wFrontSpans(long wpawns) {return nortOne (nortFill(wpawns));}
	//U64 bRearSpans (U64 bpawns) {return nortOne (nortFill(bpawns));}
	static long bFrontSpans(long bpawns) {return soutOne (soutFill(bpawns));}
	//U64 wRearSpans (U64 wpawns) {return soutOne (soutFill(wpawns));}
	
	// pawns with at least one pawn in front on the same file
	//U64 wPawnsBehindOwn(U64 wpawns) {return wpawns & wRearSpans(wpawns);}
	 
	// pawns with at least one pawn behind on the same file
	static long wPawnsInfrontOwn (long wpawns) 
	{
		return wpawns & wFrontSpans(wpawns);
		
	}
	static long bPawnsInfrontOwn (long bpawns) 
	{
		return bpawns & bFrontSpans(bpawns);
		
	}
	public static int doubledPawnsWhite(long pawns) {
		return Long.bitCount(wPawnsInfrontOwn(pawns));
	}
	public static int doubledPawnsBlack(long pawns) {
		return Long.bitCount(bPawnsInfrontOwn(pawns));
	}
	public static int isolatedPawnsWhite(long whitePawns, long blackPawns)
	{
		long c = ((whitePawns & ~0x0101010101010101L ) << 7);   //capture
		long d = ((whitePawns & ~0x8080808080808080L)  << 9 );  //capture
		
		long pawnAttacksWhite   = c | d;
		return Long.bitCount(whitePawns & ~ soutFill(nortFill(pawnAttacksWhite)));
	}
	
	public static int isolatedPawnsBlack(long whitePawns, long blackPawns)
	{
		long c = ((blackPawns & ~0x8080808080808080L) >>> 7 );
		long d = ((blackPawns & ~0x0101010101010101L) >>> 9 );
		long pawnAttacksBlack = c | d;
		
		return Long.bitCount(blackPawns & ~ soutFill(nortFill(pawnAttacksBlack)));
	}
	
	public static long passedPawnsWhite(long whitePawns, long blackPawns) {
		long c = ((blackPawns & ~0x8080808080808080L) >>> 7 );
		long d = ((blackPawns & ~0x0101010101010101L) >>> 9 );
		long pawnAttacksBlack = c | d;
		
		long blackdown  = soutFill(blackPawns | pawnAttacksBlack);
		long whitePassers = whitePawns & ~blackdown;
		return whitePassers;
		//return Long.bitCount(whitePassers);
	}
	
	public static long passedPawnsBlack(long whitePawns, long blackPawns) {
		long c = ((whitePawns & ~0x0101010101010101L ) << 7);   //capture
		long d = ((whitePawns & ~0x8080808080808080L)  << 9 );  //capture
		
		long pawnAttacksWhite   = c | d;
		
		long whiteup  = nortFill(whitePawns | pawnAttacksWhite);
		long blackPassers = blackPawns & ~whiteup;
		return blackPassers;
		//return Long.bitCount(blackPassers);
		
	}
	
	//http://chessprogramming.wikispaces.com/Pawn+Fills#FileFill
	public static long nortFill(long gen) {
		gen |= (gen <<  8);
		gen |= (gen << 16);
		gen |= (gen << 32);
		return gen;
	}

	public static long soutFill(long gen) {
		gen |= (gen >>>  8);
		gen |= (gen >>> 16);
		gen |= (gen >>> 32);
		return gen;
	}
	public static long  fileFill(long gen) {
		   return nortFill(gen) | soutFill(gen);
	}
	
	public static long openFiles(long wpawns, long bpawns) {
		   return ~fileFill(wpawns | bpawns);
	}
	public static long halfOpenOrOpenFile(long gen) {return ~fileFill(gen);}
	
	public static long wHalfOpenFile(long wpawns, long bpawns) {
		   return halfOpenOrOpenFile(wpawns)
		        ^ openFiles(wpawns, bpawns);
	}
	public static long bHalfOpenFile(long wpawns, long bpawns) {
		   return halfOpenOrOpenFile(bpawns)
		        ^ openFiles(wpawns, bpawns);
	}
} 
